package jpavlich.cardsorter;

import java.awt.Color;
import java.awt.Image;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.awt.geom.Point2D;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;

import javax.swing.ImageIcon;

import jpavlich.cardsorter.action.Action;
import jpavlich.cardsorter.action.Experiment;
import jpavlich.cardsorter.action.CenterCameraOnGlyph;
import jpavlich.cardsorter.action.Record;
import jpavlich.cardsorter.action.ResourceManager;
import jpavlich.cardsorter.action.Stop;
import jpavlich.cardsorter.action.ZoomToFit;

import org.icepdf.core.pobjects.Document;

import fr.inria.zvtm.animation.Animation;
import fr.inria.zvtm.animation.EndAction;
import fr.inria.zvtm.animation.interpolation.IdentityInterpolator;
import fr.inria.zvtm.engine.ActionListener;
import fr.inria.zvtm.engine.Button;
import fr.inria.zvtm.engine.Camera;
import fr.inria.zvtm.engine.Dragger;
import fr.inria.zvtm.engine.Location;
import fr.inria.zvtm.engine.Picker;
import fr.inria.zvtm.engine.RecordableDragger;
import fr.inria.zvtm.engine.Toolbar;
import fr.inria.zvtm.engine.VCursor;
import fr.inria.zvtm.engine.View;
import fr.inria.zvtm.engine.ViewPanel;
import fr.inria.zvtm.engine.VirtualSpace;
import fr.inria.zvtm.engine.VirtualSpaceManager;
import fr.inria.zvtm.event.ViewAdapter;
import fr.inria.zvtm.glyphs.Glyph;
import fr.inria.zvtm.glyphs.IcePDFPageImg;

public class CardSorter extends ViewAdapter {
	private boolean isDragging = false;

	private Point2D.Double lastPos = new Point2D.Double();
	private Point2D.Double absPos = new Point2D.Double();

	private VirtualSpaceManager vsm = VirtualSpaceManager.INSTANCE;
	private VirtualSpace vs;
	private Toolbar toolbar;
	private Camera camera;
	private View view;
	private Camera toolbarCamera;
	private Dragger dragger;
	private Experiment experiment;
	private ResourceManager resourceManager = ResourceManager.INSTANCE;

	private float detailFactor = 2;

	private int cardWidth;
	
	private Persistence persistence;

	CardSorter(Experiment experiment) {
		this.experiment = experiment;
		this.persistence = experiment.getPersistence();
		init();
	}

	public void init() {
		vs = vsm.addVirtualSpace("vs");
		resourceManager.registerResource(vs);
		toolbar = Toolbar.createNew("vsToolbar");
		camera = vs.addCamera();
		resourceManager.registerResource(camera);
		toolbarCamera = toolbar.addCamera();
		List<Camera> cameras = new ArrayList<Camera>();
		cameras.add(camera);
		cameras.add(toolbarCamera);
		camera.setZoomFloor(-100f);
		toolbarCamera.setZoomFloor(-100f);
		view = vsm.addPanelView(cameras, "Card Sorter", View.STD_VIEW, 1024,
				768);
		resourceManager.registerResource(view);
		// view = vsm.addFrameView(cameras, "Card Sorter", viewType, 1024, 768,
		// false, true, true, null);
		view.setBackgroundColor(Color.LIGHT_GRAY);
		view.setListener(this);
		view.setNotifyCursorMoved(true);
		dragger = new RecordableDragger(view, vs, experiment, resourceManager);
		initCards();

		view.getGlobalView(camera, 500, 2f);
	}

	private void updateAbsPos(Camera c) {
		lastPos = absPos;
		absPos = view.getCursor().getVSCoordinates(c);
		absPos.x -= c.vx;
		absPos.y -= c.vy;
	}

	// private void zoomToFitAll() {
	// view.getGlobalView(camera, 500, 2f);
	// }

	private void populateToolbar() {
		Button b = new Button(0, 0, 0, new ZoomToFit(2, 500, view, camera), experiment, "Ver todo", (new ImageIcon(
				"data/fitButton.png")).getImage());
		toolbar.addButton(b);
		b = new Button(0, 0, 0, new Action[] {
				new Stop(experiment),
				new Record(experiment) }, experiment, null,
				new Image[] { (new ImageIcon("data/record.png")).getImage(),
						(new ImageIcon("data/stop.png")).getImage() }) {

			@Override
			public void actionPerformed() {
				System.out.println(current);
				setCurrentAction((current + 1) % 2);
				super.actionPerformed();
			}

		};
		toolbar.addButton(b);

	}

	public void initCards() {
		vs.removeAllGlyphs();
		loadPDF(experiment.getCardsFile());
		resetCards();
	}

	public void resetCards() {
		Vector<Glyph> glyphs = vs.getAllGlyphs();
		for (int i = 0; i < glyphs.size(); i++) {
			Glyph g = glyphs.get(i);
			g.moveTo(i * cardWidth * 1.1f * detailFactor, 0);
		}
	}

	private void loadPDF(File pdfFile) {
		try {
			Document cardsDocument = new Document();
			cardsDocument.setFile(pdfFile.getAbsolutePath());
			cardWidth = (int) cardsDocument.getPageDimension(0, 0).getWidth();
			int numCards = cardsDocument.getNumberOfPages();
			for (int i = 0; i < numCards; i++) {
				IcePDFPageImg page = new IcePDFPageImg(0, 0, 0, cardsDocument,
						i, detailFactor, 1.0f);
				vs.addGlyph(page);
				resourceManager.registerResource(page);
			}
			cardsDocument.dispose();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public Glyph getPickedGlyph(ViewPanel v, int jpx, int jpy) {
		VCursor cursor = view.getCursor();
		Picker picker = cursor.getPicker();
		view.setActiveLayer(toolbarCamera);
		picker.computePickedGlyphList(this, toolbarCamera); // FIXME zoom to fit
		Glyph g = picker.lastGlyphEntered();
		view.setActiveLayer(camera);
		picker.computePickedGlyphList(this, camera);
		if (g == null) {
			g = picker.lastGlyphEntered();
		}
		return g;
	}

	@Override
	public void click1(ViewPanel v, int mod, int jpx, int jpy, int clickNumber,
			MouseEvent e) {
		Glyph g = view.getCursor().getPicker().pickOnTop(vs);
		if (g != null) {
			Location to = Util.calculateDestination(g, 1, view, camera);
			if (Location.equals(to, camera.getLocation())) {
				zoomToFit();
			} else {
				experiment.execute(new CenterCameraOnGlyph(g, 1, 500, view,
						camera), false);
			}
		}
	}

	public void zoomToFit() {
		experiment.execute(
				new ZoomToFit(2, 500, view, camera), false);
	}

	public void press1(ViewPanel v, int mod, int jpx, int jpy, MouseEvent e) {
		try {

			Glyph g = getPickedGlyph(v, jpx, jpy);
			dragger.startDrag(g);
			if (g instanceof ActionListener) {
				((ActionListener) g).actionPerformed();
			}
		} catch (NullPointerException ex) {
		}
	}

	public void release1(ViewPanel v, int mod, int jpx, int jpy, MouseEvent e) {
		isDragging = false;
		dragger.endDrag();
	}

	public void mouseDragged(ViewPanel v, int mod, int buttonNumber, int jpx,
			int jpy, MouseEvent e) {
		if (buttonNumber == 1) {
			dragger.drag();
		} else if (buttonNumber == 2) {
			moveCamera();
		}

	}

	private void moveCamera() {
		if (!isDragging) {
			updateAbsPos(camera);
		}
		isDragging = true;
		updateAbsPos(camera);
		camera.vx -= absPos.getX() - lastPos.x;
		camera.vy -= absPos.getY() - lastPos.y;
	}

	public void mouseWheelMoved(ViewPanel v, short wheelDirection, int jpx,
			int jpy, MouseWheelEvent e) {
		double a = (camera.focal + Math.abs(camera.altitude)) / camera.focal;
		if (wheelDirection == WHEEL_DOWN) {
			camera.altitudeOffset(-a * 5);
			VirtualSpaceManager.INSTANCE.repaint();
		} else {
			// wheelDirection == WHEEL_UP
			camera.altitudeOffset(a * 5);
			VirtualSpaceManager.INSTANCE.repaint();
		}
	}

	public View getView() {
		return view;
	}

	public void record() {
		resetCards();
		experiment.record();
	}

	public void stop() {
		resetCards();
		experiment.stop();
	}

	public void save() throws Exception {
		persistence.save(experiment);
	}

	public void play() {
		experiment.play();
	}

	public void test() {
		// Animation move = VirtualSpaceManager.INSTANCE
		// .getAnimationManager()
		// .getAnimationFactory()
		// .createGlyphTranslation(1000, cardManager.getFirst(), new
		// Point2D.Double(-100, -100), true,
		// IdentityInterpolator.getInstance(), new EndAction() {
		// public void execute(Object subject, Animation.Dimension dimension) {
		// Animation a2 = VirtualSpaceManager.INSTANCE
		// .getAnimationManager()
		// .getAnimationFactory()
		// .createGlyphTranslation(1000, cardManager.getFirst(), new
		// Point2D.Double(500, -100), true,
		// IdentityInterpolator.getInstance(), null);
		// VirtualSpaceManager.INSTANCE.getAnimationManager().startAnimation(a2,
		// false);
		// }
		// });
		// VirtualSpaceManager.INSTANCE.getAnimationManager().startAnimation(move,
		// false);
	}

	public boolean isRecording() {
		return experiment.isRecording();

	}

	public String getExperimentID() {
		return persistence.getSelectedExpDir().getName();
	}

}
